import {Style} from './interface'
import Box from './Box'
import RoundRect from './RoundRect'
import Circle from './Circle'
import Rect from './Rect'
import Event from './Event'
import EventTarget from './EventTarget'

export default class Stage extends EventTarget{
    readonly name: string = 'stage'
    ctx: CanvasRenderingContext2D
    x:number = 0
    y:number = 0
    width: number
    height: number
    canvasRatio: number = 1
    children: any = []
    platform: any
    constructor(opt?: any){
        super()
        if(opt){
            this.ctx = opt.ctx
            this.width = opt.width
            this.height = opt.height
            this.platform = opt.platform
            this.canvasRatio = opt.canvasRatio || 1
        }
        Stage.initEvent(this)
    }
    static initEvent(stage: Stage){
        const _g = stage.platform
        function emit(e:Event) {
            let clientX = e.clientX
            let clientY = e.clientY
            if(!clientX || !clientY){
                let touch = e.changedTouches && e.changedTouches[0]
                if(!touch) touch = e.touches && e.touches[0]
                if(!touch) throw Error('事件对象不兼容，无法找到触点')
                clientX = touch.clientX
                clientY = touch.clientY
            }
            let eventPath:any = []
            clientX = clientX * stage.canvasRatio
            clientY = clientY * stage.canvasRatio
            function re(els:any){
                let len = els.length
                for(; len--; ){
                    let el = els[len]
                    if(el.pointIn(clientX, clientY)){
                        eventPath.push(el)
                        e.target = el
                        if(el.children) re(el.children)
                        break
                    }
                }
            }
            re(stage.children)
            let len = eventPath.length
            for(;len--;){
                let evt = new Event()
                evt.type = e.type
                evt.touches = e.touches
                evt.changedTouches = e.changedTouches
                evt.target = e.target
                evt.clientX = clientX
                evt.clientY = clientY
                eventPath[len].emit(evt)
                if(!evt.bubbles) break
            }
            stage.emit(e)
        }
        function winTouchFn(e:any) {
            let evt = new Event()
            evt.type = e.type
            evt.touches = e.touches
            evt.changedTouches = e.changedTouches
            emit(evt)
        }
        let mousedown = false
        function winMouseFn(e:any) {
            let evt = new Event()
            if(e.type === 'mousedown') {
                evt.type = 'touchstart'
                mousedown = true
            }else if(e.type === 'mousemove'){
                evt.type = 'touchmove'
                if(!mousedown) return
            }else if(e.type === 'mouseup'){
                evt.type = 'touchend'
                mousedown = false
            }
            let touch = {
                clientX: e.clientX,
                clientY: e.clientY,
                screenX: e.screenX,
                screenY: e.screenY,
                pageX: e.pageX,
                pageY: e.pageY
            }
            evt.touches = [touch]
            evt.changedTouches = [touch]
            emit(evt)
        }
        if(_g && _g.onTouchStart){
            _g.onTouchStart((e:any) => {
                let evt = new Event()
                evt.type = 'touchstart'
                evt.touches = e.touches
                evt.changedTouches = e.changedTouches
                emit(evt)
            })
            _g.onTouchMove((e:any) => {
                let evt = new Event()
                evt.type = 'touchmove'
                evt.touches = e.touches
                evt.changedTouches = e.changedTouches
                emit(evt)
            })
            _g.onTouchEnd((e:any) => {
                let evt = new Event()
                evt.type = 'touchend'
                evt.touches = e.touches
                evt.changedTouches = e.changedTouches
                emit(evt)
            })
        }else{
            if(typeof window === 'object' && window.addEventListener){
                if(typeof window.ontouchstart !== 'undefined'){
                    window.addEventListener('touchstart', winTouchFn)
                    window.addEventListener('touchmove', winTouchFn)
                    window.addEventListener('touchend', winTouchFn)
                }else{
                    window.addEventListener('mousedown', winMouseFn)
                    window.addEventListener('mousemove', winMouseFn)
                    window.addEventListener('mouseup', winMouseFn)
                }
            }else{
                console.error('该环境不支持事件，可能无法操作！')
            }
        }
    }
    public create(tag:string, style?:Style){
        switch(tag){
            case 'rect':
                return new Rect(style)
            case 'box': 
                return new Box(style)
            case 'roundRect': 
                return new RoundRect(style)
            case 'circle':
                return new Circle(style)
            default:
                return null;
        }
    }
    public add(box:Box){
        box.add.call(this, box)
    }
    public remove(){
        const fn = (el: any) => {
            let len = el.children.length
            if(el.parent) el.parent = null
            for(let i=0; i<len; i++){
                if(el.children[i] && el.children[i].children) fn(el.children[i])
            }
            el.children.splice(0, len)
        }
        fn(this)
    }
    public render(){
        this.ctx.clearRect(0, 0, this.width, this.height)
        this.emit('beforeUpdate')
        for(let box of this.children){
            if(box.render) box.render(this.ctx)
        }
        this.emit('updated')
    }
}